#include "common/defines.h"
#include "messageparser.h"
#include "converse/converseabstractmessage.h"
#include "event/textmessageevent.h"
#include "event/photomessageevent.h"
#include "event/voicemessageevent.h"
#include "appchat/accountmanager.h"
#include "account/accountbuddy.h"
#include "account/accountuser.h"
#include <json.h>


#include <QVariantList>
#include <QVariant>
#include <QList>
#include <QDebug>


MessageParser::MessageParser(QObject *parent) :
    AbstractParser(parent)
{
}


AbstractParser::Type MessageParser::type() const
{
    return AbstractParser::MessageType;
}


AbstractEvent *MessageParser::parse(const QString &commandId, const QVariant &data)
{
    int cmId = commandId.toInt();
    switch (cmId) {
    case PRIVATE_CHAT_WITH_A_FRIEND:
        return parseMessage(data);
        break;

    case PRIVATE_CHAT_WITH_A_FRIEND_FROM_WEB:
        return parseMessageFromWeb(data);
        break;

    case SEND_MESSAGE_TO_CHAT_CHANEL:
        return parseMessageGroup(data);
        break;


    default:
        return NULL;
    }
}


AbstractEvent *MessageParser::parseMessage(const QVariant &data)
{
    if (data.type() != QVariant::Map) {
        qDebug() << "Data of the Message isn't a map";
        return NULL;
    }

    QVariantMap map= data.toMap();
    bool isValidData = true;
    qint64 id, sendingId;
    qint64 conversationId, createdTime, senderId, receiverId;
    QString messageString;
    isValidData = parseMessageMap(map,
                                  id,
                                  sendingId,
                                  conversationId,
                                  senderId,
                                  receiverId,
                                  createdTime,
                                  messageString);
    if (!isValidData)
        return NULL;

    // Parse type
    QVariant typeVariant;
    isValidData = parseIntFieldOfMap(map, "type", typeVariant);
    if (!isValidData)
        return NULL;

    bool ok;
    int typeInt = typeVariant.toInt(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"type\" can not be converted to int.";
        return NULL;
    }

    // Parse extdata
    if (!map.contains("extdata")) {
        qDebug() << "The field of \"extdata\" of the Message is missing";
        return NULL;
    }

    QVariant extdataVariant = map.value("extdata");

    QVariantMap chidPhotoMap;
    QVariant photoId;

    QVariantMap chidVoiceMap;
    QVariant voiceId;

    switch(typeInt) {
    case 0:
        if (extdataVariant.type() != QVariant::String) {
            qDebug() << "The field of \"extdata\" of the Text Message is incompatible.";
            return NULL;
        }
        break;

    case 1:
        if (extdataVariant.type() != QVariant::Map) {
            qDebug() << "The field of \"extdata\" of the Sticker Message is incompatible.";
            return NULL;
        }
        break;

    case 2:
        if (extdataVariant.type() != QVariant::Map) {
            qDebug() << "The field of \"extdata\" of the Photo Message is incompatible.";
            return NULL;
        }

        chidPhotoMap = extdataVariant.toMap();
        if (!chidPhotoMap.contains("photoId")) {
            qDebug() << "The field of \"photoId\" of the Photo Message is missing";
            return NULL;
        }
        photoId = chidPhotoMap.value("photoId");
        if(photoId.type() != QVariant::String) {
            qDebug() << "The field of \"photoId\" of the Photo Message is incompatible.";
            return NULL;
        }
        break;

    case 3:
        if (extdataVariant.type() != QVariant::Map) {
            qDebug() << "The field of \"extdata\" of the Voice Message is missing.";
            return NULL;
        }
        chidVoiceMap = extdataVariant.toMap();
        voiceId = chidVoiceMap.value("voiceId");
        if(voiceId.type() != QVariant::String) {
            qDebug() << "The field of \"voiceId\" of the Voice Message is incompatible.";
            return NULL;
        }
        break;

    default:
        return NULL;
    }

    Converse::AbstractMessage::Type type = (Converse::AbstractMessage::Type)typeInt;

    if (type == Converse::AbstractMessage::TextType) {
        return new TextMessageEvent(id,
                                    sendingId,
                                    conversationId,
                                    createdTime,
                                    senderId,
                                    receiverId,
                                    messageString,
                                    this);
    }

    if (type == Converse::AbstractMessage::PhotoType) {
        return new PhotoMessageEvent(id,
                                     sendingId,
                                     conversationId,
                                     createdTime,
                                     senderId,
                                     receiverId,
                                     messageString,
                                     this);
    }

    if (type == Converse::AbstractMessage::VoiceType) {
        return new VoiceMessageEvent(id,
                                     sendingId,
                                     conversationId,
                                     createdTime,
                                     senderId,
                                     receiverId,
                                     messageString,
                                     this);
    }

    return NULL;
}


AbstractEvent *MessageParser::parseMessageFromWeb(const QVariant &data)
{
    if (data.type() != QVariant::Map) {
        qDebug() << "Data of the Message isn't a map";
        return NULL;
    }

    QVariantMap map = data.toMap();

    qint64 id, sendingId;
    qint64 conversationId, createdTime, senderId, receiverId;
    QString messageString;
    bool isValidData = parseMessageMap(map,
                                       id,
                                       sendingId,
                                       conversationId,
                                       senderId,
                                       receiverId,
                                       createdTime,
                                       messageString);
    if (!isValidData)
        return NULL;

    return new TextMessageEvent(id,
                                sendingId,
                                conversationId,
                                createdTime,
                                senderId,
                                receiverId,
                                messageString,
                                this);
}

AbstractEvent *MessageParser::parseMessageGroup(const QVariant &data)
{
    qDebug() << "AbstractEvent *MessageParser::parseMessageGroup" << data;
    if (data.type() != QVariant::Map) {
        qDebug() << "Data of the Message isn't a map";
        return NULL;
    }

    QVariantMap map= data.toMap();
    bool isValidData = true;
    qint64 id, sendingId;
    qint64 conversationId, createdTime, senderId, receiverId;
    QString messageString;
    isValidData = parseMessageMap(map,
                                  id,
                                  sendingId,
                                  conversationId,
                                  senderId,
                                  receiverId,
                                  createdTime,
                                  messageString);
    if (!isValidData)
        return NULL;

    // Parse type
    QVariant typeVariant;
    isValidData = parseIntFieldOfMap(map, "type", typeVariant);
    if (!isValidData)
        return NULL;

    bool ok;
    int typeInt = typeVariant.toInt(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"type\" can not be converted to int.";
        return NULL;
    }

    // Parse extdata
    if (!map.contains("extdata")) {
        qDebug() << "The field of \"extdata\" of the Message is missing";
        return NULL;
    }

    QVariant extdataVariant = map.value("extdata");

    QVariantMap chidPhotoMap;
    QVariant photoId;

    QVariantMap chidVoiceMap;
    QVariant voiceId;

    switch(typeInt) {
    case 0:
        if (extdataVariant.type() != QVariant::String) {
            qDebug() << "The field of \"extdata\" of the Text Message is incompatible.";
            return NULL;
        }
        break;

    case 1:
        if (extdataVariant.type() != QVariant::Map) {
            qDebug() << "The field of \"extdata\" of the Sticker Message is incompatible.";
            return NULL;
        }
        break;

    case 2:
        if (extdataVariant.type() != QVariant::Map) {
            qDebug() << "The field of \"extdata\" of the Photo Message is incompatible.";
            return NULL;
        }

        chidPhotoMap = extdataVariant.toMap();
        if (!chidPhotoMap.contains("photoId")) {
            qDebug() << "The field of \"photoId\" of the Photo Message is missing";
            return NULL;
        }
        photoId = chidPhotoMap.value("photoId");
        if(photoId.type() != QVariant::String) {
            qDebug() << "The field of \"photoId\" of the Photo Message is incompatible.";
            return NULL;
        }
        break;

    case 3:
        if (extdataVariant.type() != QVariant::Map) {
            qDebug() << "The field of \"extdata\" of the Voice Message is missing.";
            return NULL;
        }
        chidVoiceMap = extdataVariant.toMap();
        voiceId = chidVoiceMap.value("voiceId");
        if(voiceId.type() != QVariant::String) {
            qDebug() << "The field of \"voiceId\" of the Voice Message is incompatible.";
            return NULL;
        }
        break;

    default:
        return NULL;
    }

    Converse::AbstractMessage::Type type = (Converse::AbstractMessage::Type)typeInt;

    if (type == Converse::AbstractMessage::TextType) {
        return new TextMessageEvent(id,
                                    sendingId,
                                    conversationId,
                                    createdTime,
                                    senderId,
                                    receiverId,
                                    messageString,
                                    this);
    }

    if (type == Converse::AbstractMessage::PhotoType) {
        return new PhotoMessageEvent(id,
                                     sendingId,
                                     conversationId,
                                     createdTime,
                                     senderId,
                                     receiverId,
                                     messageString,
                                     this);
    }

    if (type == Converse::AbstractMessage::VoiceType) {
        return new VoiceMessageEvent(id,
                                     sendingId,
                                     conversationId,
                                     createdTime,
                                     senderId,
                                     receiverId,
                                     messageString,
                                     this);
    }

    return NULL;
}


bool MessageParser::parseStringFieldOfMap(const QVariantMap &map,
                                          const QString &field,
                                          QVariant &fieldValue)
{
    if (!map.contains(field)) {
        qDebug() << QString("The field of %1 of the Message is missing").arg(field);
        return false;
    }

    QVariant value = map.value(field);
    if (value.type() != QVariant::String) {
        qDebug() << QString("The field of %1 of the Message is incompatible").arg(field);
        return false;
    }

    fieldValue = value;

    return true;
}


bool MessageParser::parseIntFieldOfMap(const QVariantMap &map,
                                       const QString &field,
                                       QVariant &fieldValue)
{
    if (!map.contains(field)) {
        qDebug() << QString("The field of %1 of the Message is missing").arg(field);
        return false;
    }

    QVariant value = map.value(field);

    fieldValue = value;
    return true;
}


bool MessageParser::parseMessageMap(const QVariantMap &map,
                                    qint64 &id,
                                    qint64 &idt,
                                    qint64 &chid,
                                    qint64 &from,
                                    qint64 &to,
                                    qint64 &tcreate,
                                    QString &msg)
{
    bool ok;
    bool isValidData = true;

    // Parse id
    QVariant idVariant;
    isValidData = parseStringFieldOfMap(map, "id", idVariant);
    if (!isValidData)
        return false;

    id = idVariant.toString().toLongLong(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"id\" can not be converted to qint64.";
        return false;
    }

    // Parse idt
    QVariant sendingIdVariant;
    isValidData = parseStringFieldOfMap(map, "idt", sendingIdVariant);
    if (!isValidData)
        return false;

    idt = sendingIdVariant.toString().toLongLong(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"sendingId\" can not be converted to long.";
        return false;
    }

    // Parse chid
    QVariant conversationIdVariant;
    isValidData = parseStringFieldOfMap(map, "chid", conversationIdVariant);
    if (!isValidData)
        return false;

    chid = conversationIdVariant.toString().toLongLong(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"conversationId\" can not be converted to int.";
        return false;
    }

    // Parse from
    QVariant senderIdVariant;
    isValidData = parseStringFieldOfMap(map, "from", senderIdVariant);
    if (!isValidData)
        return false;

    from = senderIdVariant.toString().toLongLong(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"senderId\" can not be converted to int.";
        return false;
    }

    // Parse to
    QVariant receiverIdVariant;
    isValidData = parseStringFieldOfMap(map, "to", receiverIdVariant);
    if (!isValidData)
        return false;

    to = receiverIdVariant.toString().toLongLong(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"receiverId\" can not be converted to int.";
        return NULL;
    }

    // Parse tcreate
    QVariant createdTimeVariant;
    isValidData = parseStringFieldOfMap(map, "tcreate", createdTimeVariant);
    if (!isValidData)
        return false;

    tcreate = createdTimeVariant.toString().toLongLong(&ok);
    if (!ok) {
        qDebug() << "The value of the field of \"createdTime\" can not be converted to int.";
        return false;
    }

    // Parse msg
    QVariant messageVariant;
    isValidData = parseStringFieldOfMap(map, "msg", messageVariant);
    if (!isValidData)
        return false;

    msg = messageVariant.toString();
    return true;
}
